package com.dny.asmtop.op;

import com.dny.asmtop.ASMMethodUtils;
import com.dny.asmtop.MethodContext;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.commons.GeneratorAdapter;

import java.lang.reflect.Field;
import java.util.Objects;

import static java.lang.Character.toUpperCase;
import static java.lang.String.format;
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
import static jdk.internal.org.objectweb.asm.Type.getType;
import static jdk.internal.org.objectweb.asm.commons.Method.getMethod;

/**
 * Created by jlutt on 2018-01-17.
 * 获取其他对象的静态字段
 * @author jlutt
 */
public class VarStaticField implements Variable {

  private final Class<?> owner;

  private final String fieldName;

  public VarStaticField(Class<?> owner, String fieldName) {
    this.owner = owner;
    this.fieldName = fieldName;
  }

  @Override
  public Type type(MethodContext context) {
    return typeOfFieldGetter(context, getType(owner), fieldName);
  }

  @Override
  public Type generator(MethodContext context) {
    Type ownerType = getType(owner);
    return generatorOfFieldGetter(context, ownerType, fieldName);
  }

  @Override
  public Object beginStore(MethodContext context) {
    return null;
  }

  @Override
  public void store(MethodContext context, Object storeContext, Type type) {

  }

  private static Type generatorOfFieldGetter(MethodContext context, Type ownerType, String field) {
    return fieldGetter(context, ownerType, field, true);
  }

  private static Type typeOfFieldGetter(MethodContext context, Type ownerType, String field) {
    return fieldGetter(context, ownerType, field, false);
  }

  private static Type fieldGetter(MethodContext context, Type ownerType, String field, boolean load) {
    GeneratorAdapter g = load ? context.getGeneratorAdapter() : null;

    //需要反射获取字段信息
    Class<?> argumentClass = ASMMethodUtils.getJavaType(context.getClassLoader(), ownerType);

    try {
      Field javaField = argumentClass.getField(field);
      if (isPublic(javaField.getModifiers()) && isStatic(javaField.getModifiers())) {
        Type resultType = getType(javaField.getType());
        if (g != null) {
          g.getStatic(ownerType, field, resultType);
        }
        return resultType;
      }
    } catch (NoSuchFieldException ignored) {
    }

    throw new RuntimeException(format("No public static field for class %s for field \"%s\". %s",
        ownerType.getClassName(),
        field,
        ASMMethodUtils.exceptionInGeneratedClass(context)));
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    VarStaticField that = (VarStaticField) o;
    return Objects.equals(owner, that.owner) &&
        Objects.equals(fieldName, that.fieldName);
  }

  @Override
  public int hashCode() {

    return Objects.hash(owner, fieldName);
  }
}
